﻿using System.IO;
using System.Reflection;

namespace LS.Screening.UI.Common
{
    /// <summary>
    /// Type帮助类
    /// </summary>
    public class TypeHelper
    {
        #region 变量

        private static Dictionary<string, Type> createFaildTypes;

        private static Dictionary<Type, List<MethodInfo>> staticMethodCache = new Dictionary<Type, List<MethodInfo>>();

        private static Dictionary<Type, List<MethodInfo>> methodCache = new Dictionary<Type, List<MethodInfo>>();

        private static Dictionary<string, Type> CreateFaildTypes
        {
            get
            {
                if (createFaildTypes == null)
                {
                    createFaildTypes = new Dictionary<string, Type>();
                    Type typeFromHandle = typeof(List<Guid?>);
                    createFaildTypes.Add(typeFromHandle.FullName, typeFromHandle);
                    createFaildTypes.Add("System.Collections.Generic.List`1[[System.Nullable`1[[System.Guid", typeFromHandle);
                    typeFromHandle = typeof(List<Guid>);
                    createFaildTypes.Add(typeFromHandle.FullName, typeFromHandle);
                }

                return createFaildTypes;
            }
        }

        #endregion

        #region Type

        /// <summary>
        /// 在当前应用域中获取继承于指定类型的所有类型
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="searchPix"></param>
        /// <returns></returns>
        public static List<Type> GetSubClassType<T>(string? searchPix = null) where T : class
        {
            return GetSubClassTypeFromDLL<T>(searchPix);
        }

        /// <summary>
        /// 从程序集中获取继承于指定类型的类型列表
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="assemblies"></param>
        /// <returns></returns>
        private static List<Type> GetTypeList<T>(List<Assembly> assemblies) where T : class
        {
            List<Type> list = new List<Type>();
            foreach (Assembly assembly in assemblies)
            {
                Type[] types = assembly.GetTypes();
                Type[] array = types;
                foreach (Type type in array)
                {
                    if (!type.IsAbstract && !type.ContainsGenericParameters && ((typeof(T).IsInterface && type.GetInterfaces().Contains(typeof(T))) || type.IsSubclassOf(typeof(T))) && !list.Contains(type))
                    {
                        list.Add(type);
                    }
                }
            }
            return list;
        }

        /// <summary>
        /// 在指定的DLL文件中获取继承于指定类型的所有类型
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="searchPix"></param>
        /// <returns></returns>
        public static List<Type> GetSubClassTypeFromDLL<T>(string? searchPix = null) where T : class
        {
            List<Assembly> list = new List<Assembly>();
            list.AddRange(GetAssemblyFromDLL(searchPix));
            return GetTypeList<T>(list);
        }

        /// <summary>
        /// 获取Type
        /// </summary>
        /// <param name="assemblyName"></param>
        /// <param name="fullName"></param>
        /// <returns></returns>
        public static Type GetType(string assemblyName, string fullName)
        {
            string fullName2 = fullName;
            if (CreateFaildTypes.ContainsKey(fullName2))
            {
                return CreateFaildTypes[fullName2];
            }

            if (CreateFaildTypes.Keys.Any((string p) => fullName2.StartsWith(p)))
            {
                string key = CreateFaildTypes.Keys.First((string p) => fullName2.StartsWith(p));
                return CreateFaildTypes[key];
            }

            assemblyName = assemblyName.Trim();
            fullName2 = fullName2.Trim();
            Assembly assembly = GetAssembly(assemblyName);
            if (assembly != null)
            {
                return assembly.GetType(fullName2);
            }

            foreach (Assembly item in AppDomain.CurrentDomain.GetAssemblies().Reverse())
            {
                Type type = item.GetType(fullName2);
                if (type != null)
                {
                    return type;
                }
            }

            return Type.GetType(fullName2);
        }

        /// <summary>
        /// 获取类型
        /// </summary>
        /// <param name="typeFullName"></param>
        /// <returns></returns>
        public static Type GetType(string typeFullName)
        {
            string[] array = typeFullName.Split(',', ';');
            return GetType(array[0], array[1]);
        }

        #endregion

        #region CreateInstance

        /// <summary>
        /// 在指定的程序集文件中获取指定类型的所有子类实例(子类必须包含无参的构造)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="filePath"></param>
        /// <returns></returns>
        public static List<T> GetSubClassInstance<T>(string filePath) where T : class
        {
            List<T> list = new List<T>();
            Assembly assembly = GetAssembly(filePath);
            Type[] types = assembly.GetTypes();
            Type[] array = types;
            foreach (Type type in array)
            {
                if (!type.IsAbstract && !type.ContainsGenericParameters && ((typeof(T).IsInterface && type.GetInterfaces().Contains(typeof(T))) || type.IsSubclassOf(typeof(T))))
                {
                    T val = (T)assembly.CreateInstance(type.FullName);
                    if (val != null)
                    {
                        list.Add(val);
                    }
                }
            }

            return list;
        }

        /// <summary>
        /// 在当前应用域下获取指定类型的所有子类实例(子类必须包含无参的构造) 搜索范围为当前应用程序同级目录中所有的DLL文件
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static List<T> GetSubClassInstance<T>() where T : class
        {
            List<T> list = new List<T>();
            List<Assembly> list2 = new List<Assembly>();
            string[] files = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll");
            string[] array = files;
            foreach (string assemblyFile in array)
            {
                list2.Add(Assembly.LoadFrom(assemblyFile));
            }

            list2.Add(Assembly.GetExecutingAssembly());
            foreach (Assembly item in list2)
            {
                Type[] types = item.GetTypes();
                Type[] array2 = types;
                foreach (Type type in array2)
                {
                    if (!type.IsAbstract && !type.ContainsGenericParameters && ((typeof(T).IsInterface && type.GetInterfaces().Contains(typeof(T))) || (type.IsSubclassOf(typeof(T)) && !type.IsAbstract)))
                    {
                        T val = (T)item.CreateInstance(type.FullName);
                        if (val != null)
                        {
                            list.Add(val);
                        }
                    }
                }
            }

            return list;
        }

        /// <summary>
        /// 创建对象实例
        /// </summary>
        /// <param name="assemblyName"></param>
        /// <param name="fullName"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public static object CreateInstance(string assemblyName, string fullName, params object[] args)
        {
            string fullName2 = fullName;
            Type type = null;
            if (CreateFaildTypes.ContainsKey(fullName2))
            {
                type = CreateFaildTypes[fullName2];
            }

            if (CreateFaildTypes.Keys.Any((string p) => fullName2.StartsWith(p)))
            {
                string key = CreateFaildTypes.Keys.First((string p) => fullName2.StartsWith(p));
                type = CreateFaildTypes[key];
            }

            if (type != null)
            {
                type = GetAssembly(assemblyName).GetType(fullName2);
                if (args == null || args.Count((object p) => p != null) == 0)
                {
                    return type.GetConstructor(Type.EmptyTypes).Invoke(null);
                }

                ConstructorInfo constructor = type.GetConstructor(args.Select((object p) => p.GetType()).ToArray());
                return constructor.Invoke(args);
            }

            assemblyName = assemblyName.Trim();
            fullName2 = fullName2.Trim();
            if (!assemblyName.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) && !assemblyName.EndsWith(".exe", StringComparison.OrdinalIgnoreCase))
            {
                string text = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, assemblyName + ".dll");
                if (File.Exists(text))
                {
                    assemblyName = text;
                }
                else
                {
                    text = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, assemblyName + ".exe");
                    if (File.Exists(text))
                    {
                        assemblyName = text;
                    }
                }
            }

            if (File.Exists(assemblyName))
            {
                if (args == null || args.Count((object p) => p != null) == 0)
                {
                    return Assembly.LoadFrom(assemblyName).CreateInstance(fullName2, ignoreCase: true);
                }

                Type type2 = Assembly.LoadFrom(assemblyName).GetType(fullName2, throwOnError: true, ignoreCase: true);
                ConstructorInfo constructor2 = type2.GetConstructor(args.Select((object p) => p.GetType()).ToArray());
                return constructor2.Invoke(args);
            }

            type = Type.GetType(fullName2);
            if (type == null)
            {
                throw new Exception(string.Format("Type {0} Reflecte Failed", assemblyName + "," + fullName2));
            }

            if (args == null || args.Count((object p) => p != null) == 0)
            {
                return type.GetConstructor(Type.EmptyTypes).Invoke(null);
            }

            ConstructorInfo constructor3 = type.GetConstructor(args.Select((object p) => p.GetType()).ToArray());
            return constructor3.Invoke(args);
        }

        /// <summary>
        /// 创建对象实例
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="paras"></param>
        /// <returns></returns>
        public static T CreateInstance<T>(params object[] paras) where T : class
        {
            Type typeFromHandle = typeof(T);
            ConstructorInfo constructor = typeFromHandle.GetConstructor(paras.Select((object p) => p.GetType()).ToArray());
            return (T)constructor.Invoke(paras);
        }

        /// <summary>
        /// 创建对象实例
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="assemblyName"></param>
        /// <param name="fullName"></param>
        /// <param name="paras"></param>
        /// <returns></returns>
        public static T CreateInstance<T>(string assemblyName, string fullName, params object[] paras) where T : class
        {
            return (T)CreateInstance(assemblyName, fullName, paras);
        }

        #endregion

        #region Assembly

        /// <summary>
        /// 从当前程序的DLL文件中获取程序集列表
        /// </summary>
        /// <param name="searchPix"></param>
        /// <returns></returns>
        private static List<Assembly> GetAssemblyFromDLL(string searchPix = null)
        {
            List<Assembly> list = new List<Assembly>();
            string[] files = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, searchPix ?? "*.dll");
            string[] array = files;
            foreach (string assemblyFile in array)
            {
                list.Add(Assembly.LoadFrom(assemblyFile));
            }
            return list;
        }

        /// <summary>
        /// 获取程序集信息
        /// </summary>
        /// <param name="assemblyName"></param>
        /// <returns></returns>
        public static Assembly GetAssembly(string assemblyName)
        {
            if (string.IsNullOrEmpty(assemblyName))
            {
                return null;
            }

            string empty = string.Empty;
            if (assemblyName.EndsWith(".dll") || assemblyName.EndsWith(".exe"))
            {
                empty = Path.Combine(AppHelper.WorkDirectory, assemblyName);
            }
            else
            {
                empty = Path.Combine(AppHelper.WorkDirectory, assemblyName + ".dll");
                if (!File.Exists(empty))
                {
                    empty = Path.Combine(AppHelper.WorkDirectory, assemblyName + ".exe");
                }
            }

            if (!File.Exists(empty))
            {
                Assembly assembly = Assembly.Load(assemblyName);
                if (assembly != null)
                {
                    return assembly;
                }
                return null;
            }
            return Assembly.LoadFrom(empty);
        }

        #endregion

        #region MethodInfo

        /// <summary>
        /// 获取静态方法相关信息
        /// </summary>
        /// <param name="classType"></param>
        /// <param name="methodName"></param>
        /// <param name="argsType"></param>
        /// <param name="flags"></param>
        /// <param name="typeBinder"></param>
        /// <returns></returns>
        public static MethodInfo GetStaticMethodInfo(Type classType, string methodName, Type[] argsType, BindingFlags flags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy, Binder typeBinder = null)
        {
            Type type = classType;
            if (!staticMethodCache.ContainsKey(type))
            {
                lock (staticMethodCache)
                {
                    if (!staticMethodCache.ContainsKey(type))
                    {
                        staticMethodCache.Add(type, new List<MethodInfo>());
                    }
                }
            }

            List<MethodInfo> list = staticMethodCache[type];
            if (list.Count > 0)
            {
                foreach (MethodInfo item in list)
                {
                    ParameterInfo[] parameters = item.GetParameters();
                    bool flag = true;
                    ParameterInfo[] array = parameters;
                    foreach (ParameterInfo parameterInfo in array)
                    {
                        if (!argsType.Contains(parameterInfo.ParameterType))
                        {
                            flag = false;
                            break;
                        }
                    }

                    if (flag)
                    {
                        return item;
                    }
                }
            }

            MethodInfo methodInfo = null;
            while (!type.Name.Equals("Object"))
            {
                methodInfo = ((argsType.Length == 0) ? type.GetMethod(methodName, flags, Type.DefaultBinder, Type.EmptyTypes, new ParameterModifier[0]) : type.GetMethod(methodName, flags, typeBinder, argsType, new ParameterModifier[1]
                {
                new ParameterModifier(argsType.Length)
                }));
                if (methodInfo == null)
                {
                    type = type.BaseType;
                    continue;
                }

                break;
            }

            if (methodInfo != null)
            {
                list.Add(methodInfo);
            }

            return methodInfo;
        }

        /// <summary>
        /// 获取方法相关信息
        /// </summary>
        /// <param name="classType"></param>
        /// <param name="methodName"></param>
        /// <param name="argsType"></param>
        /// <param name="flags"></param>
        /// <param name="typeBinder"></param>
        /// <returns></returns>
        public static MethodInfo GetMethodInfo(Type classType, string methodName, Type[] argsType, BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy, Binder typeBinder = null)
        {
            Type type = classType;
            if (!methodCache.ContainsKey(type))
            {
                lock (methodCache)
                {
                    if (!methodCache.ContainsKey(type))
                    {
                        methodCache.Add(type, new List<MethodInfo>());
                    }
                }
            }

            List<MethodInfo> list = methodCache[type];
            if (list.Count > 0)
            {
                foreach (MethodInfo item in list)
                {
                    ParameterInfo[] parameters = item.GetParameters();
                    bool flag = true;
                    ParameterInfo[] array = parameters;
                    foreach (ParameterInfo parameterInfo in array)
                    {
                        if (!argsType.Contains(parameterInfo.ParameterType))
                        {
                            flag = false;
                            break;
                        }
                    }

                    if (flag)
                    {
                        return item;
                    }
                }
            }

            MethodInfo methodInfo = null;
            while (!type.Name.Equals("Object"))
            {
                methodInfo = type.GetMethod(methodName, flags, typeBinder, argsType, new ParameterModifier[1]
                {
                new ParameterModifier(argsType.Length)
                });
                if (methodInfo == null)
                {
                    type = type.BaseType;
                    continue;
                }

                break;
            }

            if (methodInfo != null)
            {
                list.Add(methodInfo);
            }

            return methodInfo;
        }

        #endregion
    }
}
